
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <linux/slab.h>

#define MAJOR_NUM 123 
#define MINOR_NUM 0
 
MODULE_AUTHOR("mdy");
MODULE_DESCRIPTION("seq file system demo");
MODULE_LICENSE("GPL");

static LIST_HEAD(test_data);

#define MAXNODE 10

struct seq_demo_data_t
{
    int key;
    struct list_head list;
};

static void *proc_seq_start(struct seq_file *s, loff_t * pos)
{
	struct seq_demo_data_t* p = list_first_entry(&test_data, struct seq_demo_data_t, list);
    int i = 0;
    printk("proc_seq_start, *pos = %lld\n", *pos);

    if (*pos >= MAXNODE)
    {
        return NULL;
    }
    else
    {
        for (i = 0; i < *pos; i++)
        {
            p = list_next_entry(p, list);
        }

        return p;
    }

}

static void *proc_seq_next(struct seq_file *s, void *v, loff_t * pos)
{
	printk("proc_seq_next, *pos = %lld\n", *pos);

    //try to comment if statement, just return NULL, see what will happen?
    #if 1
    if (*pos < MAXNODE - 1)
    {
        struct seq_demo_data_t* p = (struct seq_demo_data_t*)v;
        struct seq_demo_data_t* next = list_next_entry(p, list);

        (*pos)++;
        return next;
    }
    #endif

    return NULL;
}

static void proc_seq_stop(struct seq_file *s, void *v)
{
    printk("proc_seq_stop....\n");
	return ;
}

static int proc_seq_show(struct seq_file *s, void *v)
{
    int key = ((struct seq_demo_data_t *)v)->key;
    printk("proc_seq show....\n");
    seq_printf(s, "nod key: %d", key);
    seq_putc(s, '\n');

	return 0;
}

static struct seq_operations proc_seq_ops = {
	.start = proc_seq_start,
	.next = proc_seq_next,
	.stop = proc_seq_stop,
	.show = proc_seq_show
};

static int seq_file_mod_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &proc_seq_ops);
};

static const struct file_operations proc_demo_fops = {
    .owner      = THIS_MODULE,
    .open       = seq_file_mod_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
	.release    = seq_release,
}; 

static struct proc_dir_entry *ent;
static int __init seq_file_mod_init(void)
{
    int i = 0;
    ent = proc_create("seq_demo", 0666, NULL, &proc_demo_fops);
    printk(KERN_ALERT "seq demo create...\n");

    for (i = 0; i < MAXNODE; i++)
    {
        struct seq_demo_data_t *temp = kmalloc(sizeof(struct seq_demo_data_t), GFP_KERNEL);
        temp->key = i;
        list_add_tail(&temp->list, &test_data);
    }

    return 0;
}

static void __exit seq_file_mod_exit(void)
{
    struct seq_demo_data_t* p;
    struct seq_demo_data_t* n;

    proc_remove(ent);
    printk(KERN_ALERT "proc demo remove...\n");
    
    list_for_each_entry_safe(p, n, &test_data, list)
    {
        list_del(&p->list);
        kfree(p);
    }
}
 
module_init(seq_file_mod_init);
module_exit(seq_file_mod_exit);


